package cn.car.biz.carAccess.eventListener;

import cn.car.biz.carAccess.event.GIoInResultEvent;
import cn.car.constants.CarAccessConstants;
import cn.car.domains.ctx.PlateDetectedContext;
import cn.car.domains.entity.CameraInfoEntity;
import cn.car.domains.entity.VehicleEntryExitLedgerInfoEntity;
import cn.car.domains.enums.*;
import cn.car.exception.CarAccessControlException;
import cn.car.service.CameraService;
import cn.car.service.VehicleEntryExitLedgerInfoService;
import cn.car.service.config.SystemConfig;
import cn.car.utils.CarStringUtil;
import cn.car.utils.NvrUtil;
import cn.hutool.core.date.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@Component
@Slf4j
public class GIoInResultEventListenerCutVideo implements ApplicationListener<GIoInResultEvent> {

    @Resource
    private NvrUtil nvrUtil;

    private static final Supplier<String> VIDEO_LEDGER = () -> String.join(File.separator, CarAccessConstants.getVideoFilePath().get(), "ledger");

    @Resource
    private SystemConfig systemConfig;

    @Resource
    private CameraService cameraService;

    ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(
            4
    );

    @Resource
    private VehicleEntryExitLedgerInfoService vehicleEntryExitLedgerInfoService;

    @Override
    public void onApplicationEvent(GIoInResultEvent event) {
        PlateDetectedContext context = event.getContext();
        if (context == null) {
            return;
        }

        if (!VehicleTypeEnum.ENTRY_EXIT.name().equals(context.getVehicleCommonInfoEntity().getVehicleType())) {
            return;
        }

        if (Objects.equals(context.getGIoInType(), GIoInTypeEnum.up.name())) {
            return;
        }

        CameraInfoEntity cameraInfoEntity = context.getCameraInfoEntity();
        CameraInfoEntity snapCamera = cameraService.getCameraById(cameraInfoEntity.getAssociationCameraId());

        if (snapCamera == null || !CameraWorkModeEnum.SNAP.name().equals(snapCamera.getWorkMode())) {
            throw CarAccessControlException.DATE_NOT_EXIST.newInstance("[HeadMidTailSnapshot Event] snap camera not exist detected camera ip:" + cameraInfoEntity.getCameraIp());
        }

        //剪视频
        final String fileName = CarStringUtil.generateFileName(".mp4");
        final String fileDir = String.join(File.separator, VIDEO_LEDGER.get());
        final String videoUrl = String.join(File.separator, fileDir, fileName);
        final File dest = new File(videoUrl);
        final File parentFile = dest.getParentFile();
        if (!parentFile.exists()) {
            boolean mkdirs = parentFile.mkdirs();
            if (mkdirs) {
                log.error("[MKDIR ERROR] parentFile:{}", parentFile);
            }
        }

        Date upTime = context.getUpTime();
        Date downTime = context.getDownTime();


        String nvrCutVideoMaxInterval = systemConfig.getNvrCutVideoMaxInterval().get();
        String nvrCutVideoDelayTime = systemConfig.getNvrCutVideoDelayTime().get();

        Date recordStartTime = DateUtil.offset(DateUtil.offset(upTime, DateField.SECOND, -Integer.parseInt(nvrCutVideoMaxInterval)), DateField.SECOND, -Integer.parseInt(nvrCutVideoDelayTime));
        Date recordEndTime =  DateUtil.offset(DateUtil.offset(downTime, DateField.SECOND, Integer.parseInt(nvrCutVideoMaxInterval)), DateField.SECOND, -Integer.parseInt(nvrCutVideoDelayTime));


        scheduler.schedule(() -> record(videoUrl, snapCamera.getChannelCode(), snapCamera.getNvrAddressUrl(), recordStartTime, recordEndTime), 30, TimeUnit.SECONDS);

        VehicleEntryExitLedgerInfoEntity updateEntity = new VehicleEntryExitLedgerInfoEntity();
        updateEntity.setId(context.getVehicleCommonLedgerInfoEntity().getAssociationId());

        if (InoutTypeEnum.in.name().equals(context.getInOutType())) {
            updateEntity.setEntryVideoUrl(videoUrl);
        } else {
            updateEntity.setExitVideoUrl(videoUrl);
        }
        vehicleEntryExitLedgerInfoService.update(updateEntity);

    }


    public void record(String outputFilePath, String channelCode, String nvrAddressUrl, Date startTime, Date endTime) {

        long cutVideoSecond = DateUtil.between(startTime, endTime, DateUnit.SECOND);

        String startTimeStr = DateUtil.format(startTime, "yyyyMMdd't'HHmmss'z'");
        String endTimeStr = DateUtil.format(endTime, "yyyyMMdd't'HHmmss'z'");

        final String nvrRtspUrl = nvrUtil.getNvrRtspUrl(startTimeStr, endTimeStr, nvrAddressUrl, channelCode);

        String ffmpegCommand = String.format(
                //"ffmpeg -sseof -%d -i %s -c:v copy -c:a aac -strict experimental -t %d %s",
                "ffmpeg -i \"%s\" -y -c:v copy -an -t %d %s",
                nvrRtspUrl, cutVideoSecond, outputFilePath
        );

        log.info("ffmpegCommand :{}", ffmpegCommand);
        Process process = null;
        BufferedReader reader = null;
        try {
            // 执行FFmpeg命令
            ProcessBuilder processBuilder = new ProcessBuilder(ffmpegCommand.split(" "));
            processBuilder.redirectErrorStream(true);
            process = processBuilder.start();

            // 读取并打印FFmpeg的输出
            reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                log.info(line);
            }

            // 等待进程完成
            process.waitFor();
            log.info("[NvrStreamRecorder record] success outputFilePath:{}" , outputFilePath);
        } catch (Exception e) {
            log.error("[NvrStreamRecorder record]Error executing FFmpeg command:{}", ffmpegCommand , e);
        } finally {
            try {
                if (reader != null){
                    reader.close();
                }
            } catch (IOException e) {
                log.error("[NvrStreamRecorder record] close reader error ");
            }
            if (process != null){
                process.destroy();
            }

        }
    }
}
